
#include <QPainter>
#include <QLabel>
#include <QEvent>

#include "canvasAppl.h"
#include "aplicationdraw.h"
#include "acmframe.h"
#include "imo3d.h"
#include "winzoom.h"
#include "pam.h"



//
//////////////////////////////////////////////////////////////////////////
static void createCurveFunc (void *m_objClass, Curve *cv, int _idSurf)
{
  acmFrame *curr_model = ((canvasAppl *)m_objClass)->getCurrModel ( );

  if (curr_model != NULL)
    curr_model->addCurve(cv);

  return;
}


//
//////////////////////////////////////////////////////////////////////////
static void winZoomFunc (void *m_objClass, double xmin, double ymin, double xmax, double ymax, int old_state)
{
  ((canvasAppl *)m_objClass)->setWorld (xmin, xmax, ymin, ymax);
  ((canvasAppl *)m_objClass)->setState (old_state);
  return;
}


//
//////////////////////////////////////////////////////////////////////////
static void pamFunc (void *m_objClass, double dx, double dy, int old_state)
{
  double xmin, xmax, ymin, ymax;
  ((canvasAppl *)m_objClass)->getWindow (&xmin, &xmax, &ymin, &ymax);
  ((canvasAppl *)m_objClass)->setWorld (xmin+dx, xmax+dx, ymin+dy, ymax+dy);
  ((canvasAppl *)m_objClass)->setState (old_state);
  return;
}

//
//////////////////////////////////////////////////////////////////////////
canvasAppl::canvasAppl(QWidget *parent)
  : canvasgl(parent)
{
  // initialize variables
  m_grid = false;
  m_axis = true;

  m_grid_step = 1.0;
  m_grid_orig_x = 0.0;
  m_grid_orig_y = 0.0;
  m_snap_to_grid = 0;

  currDraw = new aplicationdraw;

  m_currModel = NULL;
  m_positionLabel = m_messageLabel = NULL;

  m_state = 0;
}

//
//////////////////////////////////////////////////////////////////////////
canvasAppl::~canvasAppl()
{

  delete currDraw;
}

//
//////////////////////////////////////////////////////////////////////////
void canvasAppl::redraw( QPainter *g )
{
  if (currDraw == NULL)
    currDraw = new aplicationdraw (g);
  else
    currDraw->setCanvas(g);

  double ref_size = computeRefSize ( );
  currDraw->ref_size = ref_size;

  displayAxis(currDraw);
  displayGrid(currDraw);

  // drawToler (currDraw, 0.0, 0.0);


  if (m_currModel == NULL)
    return;

  m_currModel->setDraw (currDraw);
  m_currModel->draw ( );


  if( IMO.isActive() && m_state == 1)
    IMO.drawCurrCurve();

  if (ZOOM.isActive() && m_state == 2)
    ZOOM.draw();


}

//
//////////////////////////////////////////////////////////////////////////
void canvasAppl::mousePressedWC( QMouseEvent *e, QPointF *pt )
{
  if (m_currModel == NULL)
    return;

  // set selection state
  if (e->button() == Qt::RightButton)
  {
    if (IMO.isActive() && m_state == 1)
      IMO.endAddPoint ( );
    selectEntity ( );
  }


  // check current key
  if (e->button() == Qt::LeftButton) // left button
  {
    if( IMO.isActive() && m_state == 1)  // add curve
      IMO.addPoint (pt->x(), pt->y(), 0.0);
    else if( ZOOM.isActive() && m_state == 2)  // zoom
      ZOOM.addPoint (pt->x(), pt->y());
    else if (m_state == 0) // pick
    {
      bool add = false;  // add entity to pick
      if (shift_key)
        add = true;
      m_currModel->pick(pt->x(), pt->y(), add, this->getTol());
    }
  }

  // pam windows
  if (e->button() == Qt::MidButton) // middle button
  {
    double xmin, xmax, ymin, ymax;
    getWindow (&xmin, &xmax, &ymin, &ymax);

    PAM.active ( );
    // PAM.setDraw (currDraw);
    PAM.setCurrState (m_state);
    PAM.setEndAction((void *) this, pamFunc);
    PAM.addPoint (pt->x(), pt->y());
    m_state = 4;
  }


  //drawToler (currDraw, pt->x(), pt->y());


}

//
//////////////////////////////////////////////////////////////////////////
void canvasAppl::mouseDraggedWC( QMouseEvent *e, QPointF *pt )
{
  static char text[254];
  if (m_positionLabel != NULL)
  {
    sprintf (text, "X: %10.7f, Y: %10.7f", pt->x(), pt->y()); 
    m_positionLabel->setText(text);
  }

  if (m_currModel == NULL)
    return;

  // here we draw the temporary collected curve in case the user is collecting a curve
  if( IMO.isActive()  && m_state == 1)
  {
    IMO.addTempPoint(pt->x(), pt->y(), 0.0);
    this->refreshPixmap();
  }

  if( ZOOM.isActive() && m_state == 2)
  {
    ZOOM.addTempPoint(pt->x(), pt->y());
    this->refreshPixmap();
  }

  if( PAM.isActive() && m_state == 4)
  {
    PAM.addTempPoint(pt->x(), pt->y());
    // m_messageLabel->setText (PAM.getMessage());
    this->refreshPixmap();
  }

}

//
//////////////////////////////////////////////////////////////////////////
void canvasAppl::mouseReleasedWC( QMouseEvent *e, QPointF *pt )
{
  if (m_currModel == NULL)
    return;

  // check current key
  if (e->button() == Qt::LeftButton) // left button
  {
    if( ZOOM.isActive() && m_state == 2)  // zoom
      ZOOM.addPoint (pt->x(), pt->y());
  }

    // pam windows
  if (e->button() == Qt::MidButton) // middle button
  {
    if( PAM.isActive() && m_state == 4)  // zoom
      PAM.addPoint (pt->x(), pt->y());
  }

}

//
//////////////////////////////////////////////////////////////////////////
void canvasAppl::findMaxMin( double *xmin, double *xmax, double *ymin, double *ymax )
{
  if (m_currModel == NULL)
  {
    *xmin = *ymin = 0.0;
    *xmax = *ymax = 10.0;
  }
  else
  {
    m_currModel->getMaxMin( xmin, xmax, ymin, ymax );
  }
}

//
//////////////////////////////////////////////////////////////////////////
void canvasAppl::displayAxis( drawGRA *g )
{
  double  dir_x[14] = {-0.1,0.1, 0.6, 0.1, 0.6,0.2, 1,0, 0.6,-0.2, 0.6, -0.1, -0.1,-0.1};
  double  dir_y[14] = {0.1,-0.1, 0.1, 0.6, 0.2,0.6, 0,1, -0.2,0.6, -0.1, 0.6, -0.1,-0.1};

  double pos_txt[6] = {0.75,-0.3, -0.3,0.75, -1.0,-0.4};

  double   xmin, xmax;
  double   ymin, ymax;
  double   arrow_size; 
  int      i;

  this->getWindow(&xmin, &xmax, &ymin, &ymax);
  arrow_size = (ymax - ymin) / 12.0;

  for (i = 0; i < 7; i++)
  {
    dir_x[i*2+0] = dir_x[i*2+0] * arrow_size;
    dir_x[i*2+1] = dir_x[i*2+1] * arrow_size;
    dir_y[i*2+0] = dir_y[i*2+0] * arrow_size;
    dir_y[i*2+1] = dir_y[i*2+1] * arrow_size;
  }
  for (i = 0; i < 3; i++)
  {
    pos_txt[i*2+1] = pos_txt[i*2+1] * arrow_size;
    pos_txt[i*2+0] = pos_txt[i*2+0] * arrow_size;
  }


  // draw
  g->SetLineColor (draw_BLUE);
  g->PolyLine (7, dir_x);
  g->PolyLine (7, dir_y);
  g->SetTextColor (draw_BLUE);
  g->text (pos_txt[0], pos_txt[1], 0.0, "x");
  g->text (pos_txt[2], pos_txt[3], 0.0, "y");
  g->text (pos_txt[4], pos_txt[5], 0.0, "(0,0)");
}

//
//////////////////////////////////////////////////////////////////////////
void canvasAppl::displayGrid( drawGRA *g )
{
 double   hsize_mm, hsize_wc;
 double   x, y;
 double   xmin, xmax;
 double   ymin, ymax;
 double   gxmn, gxmx;
 double   gymn, gymx;
 double   density;
 double   pts[2];

/* Get canvas horizontal size in millimeters and
 * window in world coordinates.
 */
 hsize_mm = height();
 this->getWindow(&xmin, &xmax, &ymin, &ymax);
 hsize_wc = xmax - xmin;
 density = m_grid_step * (hsize_mm / hsize_wc);

/* Do not display grid if grid point spacing is less
 * then 2 millimeters.
 */
 if( density < 2.0 )
  return;

/* Calculate upper and lower limits to grid display
 */
 gxmn = (int)((xmin-m_grid_orig_x)/m_grid_step) * m_grid_step +
        m_grid_orig_x;
 gxmx = (int)((xmax-m_grid_orig_x)/m_grid_step) * m_grid_step + m_grid_step +
        m_grid_orig_x;
 gymn = (int)((ymin-m_grid_orig_y)/m_grid_step) * m_grid_step +
        m_grid_orig_y;
 gymx = (int)((ymax-m_grid_orig_y)/m_grid_step) * m_grid_step + m_grid_step +
        m_grid_orig_y;

/* Draw grid points
 */
 g->SetMarkType (drawGRA::draw_POINT);
 g->SetMarkSize (1);
 g->SetMarkColor (draw_BLACK);
 for( x = gxmn; x <= gxmx; x += m_grid_step )
  for( y = gymn; y <= gymx; y += m_grid_step )
  {
    pts[0] = x; pts[1] = y;
    g->PolyMark (1, pts);
  }
}

//
//////////////////////////////////////////////////////////////////////////
double canvasAppl::computeRefSize()
{
 double   dx, dy, sizew;
 double   xmin, xmax, ymin, ymax;

/* Get canvas horizontal size in millimeters and
 * window in world coordinates.
 */
 this->getWindow(&xmin, &xmax, &ymin, &ymax);
 dx = xmax - xmin;
 dy = ymax - ymin;
 sizew = sqrt( dx*dx + dy*dy );
 return ( sizew );
}

//
//////////////////////////////////////////////////////////////////////////
void canvasAppl::setModel( acmFrame *model )
{
  m_currModel = model;
}

//
//////////////////////////////////////////////////////////////////////////
void canvasAppl::setMessAndPosLabel( QLabel *messageLabel, QLabel *positionLabel )
{
  m_messageLabel = messageLabel;
  m_positionLabel = positionLabel;
}

//
//////////////////////////////////////////////////////////////////////////
void canvasAppl::createCurve( char *_type )
{
  // set imo3d
  if( !IMO.isInitialized() )
  {
    IMO.setDrawCurve(currDraw);
    IMO.setEndAction((void *) this, createCurveFunc);
    IMO.initialize();
  }

  // init curve
  IMO.beginCreate (_type, 0.0, 0.0, 1.0);

  m_state = 1;

}

//
//////////////////////////////////////////////////////////////////////////
void canvasAppl::editEntity( char *_type )
{

  m_state = 3;
}


//
//////////////////////////////////////////////////////////////////////////
void canvasAppl::selectEntity ( )
{
  if (IMO.isActive())
    IMO.releaseCurrCurve ( );
  m_state = 0;
}

//
//////////////////////////////////////////////////////////////////////////
void canvasAppl::zoomWin ( )
{
  ZOOM.active ( );
  ZOOM.setDraw (currDraw);
  ZOOM.setCurrState (m_state);
  ZOOM.setEndAction((void *) this, winZoomFunc);
  m_state = 2;
}

//
//////////////////////////////////////////////////////////////////////////
void canvasAppl::drawToler (drawGRA *m_objdraw, double x, double y)
{
  double toler= this->getTol();
    m_objdraw->SetLineColor (draw_CYAN);

    m_objdraw->begin ( );
    m_objdraw->addVertex (x-toler, y-toler, 0);
    m_objdraw->addVertex (x-toler, y+toler, 0);
    m_objdraw->end ( );

    m_objdraw->begin ( );
    m_objdraw->addVertex (x+toler, y-toler, 0);
    m_objdraw->addVertex (x+toler, y+toler, 0);
    m_objdraw->end ( );

    m_objdraw->begin ( );
    m_objdraw->addVertex (x+toler, y-toler, 0);
    m_objdraw->addVertex (x-toler, y-toler, 0);
    m_objdraw->end ( );

    m_objdraw->begin ( );
    m_objdraw->addVertex (x+toler, y+toler, 0);
    m_objdraw->addVertex (x-toler, y+toler, 0);
    m_objdraw->end ( );
}

